package ru.ifmo.thesis.suite;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.ifmo.thesis.domain.Job;
import ru.ifmo.thesis.domain.Shedule;
import ru.ifmo.thesis.domain.Worker;
import ru.ifmo.thesis.gp.MyConfigurationOptions;
import ru.ifmo.thesis.gp.MyConfigurationOptionsBuilder;
import ru.ifmo.thesis.quantities.Minutes;
import ru.ifmo.thesis.quantities.Percent;
import ru.ifmo.thesis.solver.gp.FixedTimeCalculationRule;
import ru.ifmo.thesis.solver.gp.GPSolver;
import ru.ifmo.thesis.task.Task;
import ru.ifmo.thesis.task.TaskGenerationOptions;
import ru.ifmo.thesis.task.TaskGenerator;
import ru.ifmo.thesis.util.Algorithms;
import ru.ifmo.thesis.util.Loggers;

public class Incrementing {
	
	private static final Logger log = LoggerFactory.getLogger(Incrementing.class);

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Loggers.initLoggerFromConfig();
		log.debug("Starting");
		
		TaskGenerator generator = new TaskGenerator();
		Task t = generator.generateTask(new TaskGenerationOptions()
				.setGenerateContractsCount(500)
				.setGenerateLocationsCount(100)
				.setLocationGenRadius(30)
				.setTaskGenerationCount(100)
				.setWorkersCount(30));
		
		List<Job> toAdd = new ArrayList<Job>();
		
		for (int i = 0; i < 10; i++) {
			toAdd.add(t.getJobs().remove(0));
		}
		
		MyConfigurationOptionsBuilder opBuilder = new MyConfigurationOptionsBuilder();
		MyConfigurationOptions options = opBuilder
			.setOfPopulation(360)
			.setTakePercentToNewPopulation(
					new Percent(0.66))
			.setMovingsPerMutation(
					1)
			.setMutationPercent(0.3)
			.setCrossoverRate(new Percent(1.0))
			.build();
		
		GPSolver solver = new GPSolver(new FixedTimeCalculationRule(new Minutes(3)), options);
		
		Shedule schedule = solver.solve(t);
		Shedule currentShedule = new Shedule(schedule);
		Shedule currentHeuristic = currentShedule;
		
		log.debug("Schedule cost: " + Algorithms.calculateSheduleCost(schedule));
			
		int i = 0;
		while (!toAdd.isEmpty()) {
			log.debug("Iteration #" + i++);
			Job currentJob = toAdd.remove(0);
			Shedule heuristic = heuristic(currentHeuristic, currentJob);
			Shedule gaRandom = gaRandom(t, currentJob, solver);
			solver.reset();
//			Shedule gaPredefined = gaPredefined(t, currentJob, solver, currentShedule);
			solver.reset();
			
			log("H:", heuristic);
			log("R:", gaRandom);
//			log("P:", gaPredefined);
			
			currentHeuristic = heuristic;
			currentShedule = best(gaRandom, gaRandom);
		}
	}

	private static Shedule best(Shedule first, Shedule second) {
		return (Algorithms.calculateSheduleCost(first) < Algorithms.calculateSheduleCost(second)) 
			? first : second;
	}

	private static void log(String prefix, Shedule schedule) {
		log.debug(prefix+Algorithms.calculateSheduleCost(schedule));
	}

	private static Shedule gaPredefined(Task t, Job currentJob, GPSolver solver, Shedule seed) {
		Task newTask = cloneTask(t);
		
		newTask.getJobs().add(currentJob);

		return solver.solve(newTask, seed);
	}

	private static Shedule gaRandom(Task t, Job currentJob, GPSolver solver) {
		Task newTask = cloneTask(t);
		
		newTask.getJobs().add(currentJob);

		return solver.solve(newTask);
	}

	private static Task cloneTask(Task t) {
		Task newTask = new Task(
				new ArrayList<Worker>(t.getWorkers()), 
				new ArrayList<Job>(t.getJobs()));
		return newTask;
	}

	private static Shedule heuristic(Shedule currentShedule, Job currentJob) {
		Shedule best = null;
		for (Worker w : currentShedule.getWorkers()) {
			for (int i = 0; i < currentShedule.getPersonalShedule(w).size(); i++) {
				Shedule copy = new Shedule(currentShedule);
				List<Job> personalShedule = copy.getPersonalShedule(w);
				personalShedule.add(i, currentJob);
				copy.setPersonalShedule(w, personalShedule);
			
				if (best == null) {
					best = copy;
				} else {
					if (Algorithms.calculateSheduleCost(copy) < Algorithms.calculateSheduleCost(best)) {
						best = copy; 
					}
				}
			}
		}
		return best;
	}

}
